// addrspace.cc 
//	Routines to manage address spaces (executing user programs).
//
//	In order to run a user program, you must:
//
//	1. link with the -N -T 0 option 
//	2. run coff2noff to convert the object file to Nachos format
//		(Nachos object code format is essentially just a simpler
//		version of the UNIX executable object code format)
//	3. load the NOFF file into the Nachos file system
//		(if you haven't implemented the file system yet, you
//		don't need to do this last step)
//
// Copyright (c) 1992-1993 The Regents of the University of California.
// All rights reserved.  See copyright.h for copyright notice and limitation 
// of liability and disclaimer of warranty provisions.

#include "copyright.h"
#include "system.h"
#include "addrspace.h"
//#include "noff.h"
#include "table.h"
#include "synch.h"

/**************************************
changes related to project II
**************************************/
#include "iostream"
/**************************************
changes related to project II
**************************************/

extern "C" { int bzero(char *, int); }	

#ifdef CHANGED
/**************************************
changes related to project II
**************************************/
/*BitMap * pageBitMapTable = new BitMap(NumPhysPages);*/
ProcessTable myProcessTable[MAX_NUM_PROCESSES];
Lock *PageTableLock[MAX_NUM_PROCESSES];
Lock *ProcessTableLock = new Lock("ProcessTableLock");
Lock *popProcessTableonFork = new Lock("PopulateProcessTableonFork");
int userProcesses = 0;
int activeThreads = 0;
int totalThreads = 0;
int ID;
/**************************************
changes related to project II
**************************************/
#endif

Table::Table(int s) : map(s), table(0), lock(0), size(s) {
    table = new void *[size];
    lock = new Lock("TableLock");
}

Table::~Table() {
    if (table) {
	delete table;
	table = 0;
    }
    if (lock) {
	delete lock;
	lock = 0;
    }
}

void *Table::Get(int i) {
    // Return the element associated with the given if, or 0 if
    // there is none.

    return (i >=0 && i < size && map.Test(i)) ? table[i] : 0;
}

int Table::Put(void *f) {
    // Put the element in the table and return the slot it used.  Use a
    // lock so 2 files don't get the same space.
    int i;	// to find the next slot

    lock->Acquire();
    i = map.Find();
    lock->Release();
    if ( i != -1)
	table[i] = f;
    return i;
}

void *Table::Remove(int i) {
    // Remove the element associated with identifier i from the table,
    // and return it.

    void *f =0;

    if ( i >= 0 && i < size ) {
	lock->Acquire();
	if ( map.Test(i) ) {
	    map.Clear(i);
	    f = table[i];
	    table[i] = 0;
	}
	lock->Release();
    }
    return f;
}

//----------------------------------------------------------------------
// SwapHeader
// 	Do little endian to big endian conversion on the bytes in the 
//	object file header, in case the file was generated on a little
//	endian machine, and we're now running on a big endian machine.
//----------------------------------------------------------------------

static void 
SwapHeader (NoffHeader *noffH)
{
	noffH->noffMagic = WordToHost(noffH->noffMagic);
	noffH->code.size = WordToHost(noffH->code.size);
	noffH->code.virtualAddr = WordToHost(noffH->code.virtualAddr);
	noffH->code.inFileAddr = WordToHost(noffH->code.inFileAddr);
	noffH->initData.size = WordToHost(noffH->initData.size);
	noffH->initData.virtualAddr = WordToHost(noffH->initData.virtualAddr);
	noffH->initData.inFileAddr = WordToHost(noffH->initData.inFileAddr);
	noffH->uninitData.size = WordToHost(noffH->uninitData.size);
	noffH->uninitData.virtualAddr = WordToHost(noffH->uninitData.virtualAddr);
	noffH->uninitData.inFileAddr = WordToHost(noffH->uninitData.inFileAddr);
}

//----------------------------------------------------------------------
// AddrSpace::AddrSpace
// 	Create an address space to run a user program.
//	Load the program from a file "executable", and set everything
//	up so that we can start executing user instructions.
//
//	Assumes that the object code file is in NOFF format.
//
//	"executable" is the file containing the object code to load into memory
//
//      It's possible to fail to fully construct the address space for
//      several reasons, including being unable to allocate memory,
//      and being unable to read key parts of the executable.
//      Incompletely consretucted address spaces have the member
//      constructed set to false.
//----------------------------------------------------------------------

AddrSpace::AddrSpace(OpenFile *executable) : fileTable(MaxOpenFiles) {
  //NoffHeader noffH;
  
  unsigned int i, j, size, codeSize, initSize;
  int nAvailablePhysicalPage = -1;
	
	
#ifdef CHANGED
  int DataSizeToBeCopied;
  ID = userProcesses;   																//	Assign number of running process as ID
	PageTableLock[ID] = new Lock("PageTableLock");	//	Lock for each Process Table Entry
	PageTableLock[ID]->Acquire();
#endif

//	Don't allocate the input or output to disk files
  fileTable.Put(0);
  fileTable.Put(0);
  
  executable->ReadAt((char *)&noffH, sizeof(noffH), 0);

  if ((noffH.noffMagic != NOFFMAGIC) && (WordToHost(noffH.noffMagic) == NOFFMAGIC))
    	SwapHeader(&noffH);
  ASSERT(noffH.noffMagic == NOFFMAGIC);

  execFile = executable; 
  size = noffH.code.size + noffH.initData.size + noffH.uninitData.size ;
  numPages = divRoundUp(size, PageSize) + divRoundUp(UserStackSize,PageSize); 
  numStackPages = divRoundUp(UserStackSize,PageSize); 	
  numCodeExecPages = divRoundUp(noffH.code.size+noffH.initData.size,PageSize);  
                                    				
									
  size = numPages * PageSize;
  newnumpages = numPages + (MAX_NUM_THREADS-1)*numStackPages;

  extpagetable = new ExtendedPageTable[newnumpages];

  for (int v = 0; v < newnumpages; v++) 
  {
	 
	if(v<numCodeExecPages){ 
		extpagetable[v].pagestatus = exec;
		extpagetable[v].ExecLoc = noffH.code.inFileAddr+v*PageSize;
		//saving exec location - file offset
	} 
	else{
		extpagetable[v].pagestatus = udata; //it is not in executable
	}
	
	PageTableLock[ID]->Release();	
	ProcessTableLock->Acquire();
	InitializeProcessThreadTable(ID);
	AddressSpaceIndex = PopulateProcessTable(ID);				
 	ProcessTableLock->Release();

	/*if (noffH.code.size > 0) {
        DEBUG('a', "Initializing code segment, at 0x%x, size %d\n", 
			noffH.code.virtualAddr, noffH.code.size);
        executable->ReadAt(&(machine->mainMemory[noffH.code.virtualAddr]),
			noffH.code.size, noffH.code.inFileAddr);
    }
    if (noffH.initData.size > 0) {
        DEBUG('a', "Initializing data segment, at 0x%x, size %d\n", 
			noffH.initData.virtualAddr, noffH.initData.size);
        executable->ReadAt(&(machine->mainMemory[noffH.initData.virtualAddr]),
			noffH.initData.size, noffH.initData.inFileAddr);
    }*/


}}

//----------------------------------------------------------------------
// AddrSpace::~AddrSpace
//
// 	Dealloate an address space.  release pages, page tables, files
// 	and file tables
//----------------------------------------------------------------------

AddrSpace::~AddrSpace()
{
    delete pageTable;
    delete execFile;
    delete extpagetable;
}

//----------------------------------------------------------------------
// AddrSpace::getID()
//
// 	returns process ID
//----------------------------------------------------------------------
int AddrSpace::getID(){
  return ID;
}

//----------------------------------------------------------------------
// AddrSpace::InitRegisters
// 	Set the initial values for the user-level register set.
//
// 	We write these directly into the "machine" registers, so
//	that we can immediately jump to user code.  Note that these
//	will be saved/restored into the currentThread->userRegisters
//	when this thread is context switched out.
//----------------------------------------------------------------------

void
AddrSpace::InitRegisters()
{
    int i;

    for (i = 0; i < NumTotalRegs; i++)
	machine->WriteRegister(i, 0);

    // Initial program counter -- must be location of "Start"
    machine->WriteRegister(PCReg, 0);	

    // Need to also tell MIPS where next instruction is, because
    // of branch delay possibility
    machine->WriteRegister(NextPCReg, 4);

   // Set the stack register to the end of the address space, where we
   // allocated the stack; but subtract off a bit, to make sure we don't
   // accidentally reference off the end!
    machine->WriteRegister(StackReg, numPages * PageSize - 16);
    DEBUG('a', "Initializing stack register to %x\n", numPages * PageSize - 16);
}

//----------------------------------------------------------------------
// AddrSpace::SaveState
// 	On a context switch, save any machine state, specific
//	to this address space, that needs saving.
//----------------------------------------------------------------------

void AddrSpace::SaveState() 
{
//if there is a dirty page in TLB, propagate dirty bit back to IPT
for(int i=0; i<TLBSize; i++){

	if(machine->tlb[i].valid && machine->tlb[i].dirty){//for all valid entries in tlb
		
		IPT[machine->tlb[i].physicalPage].dirty = machine->tlb[i].dirty;
		machine->tlb[i].valid =FALSE;
	}

}
}
//----------------------------------------------------------------------
// AddrSpace::RestoreState
// 	On a context switch, restore the machine state so that
//	this address space can run
//----------------------------------------------------------------------

void AddrSpace::RestoreState() 
{
for(int i=0; i<TLBSize; i++){
if(machine->tlb[i].valid){//for all valid entries in tlb invalidate tlb
		machine->tlb[i].valid =FALSE;
	} 
}
}
#ifdef CHANGED


//----------------------------------------------------------------------
// AddrSpace::InitializeProcessThreadTable
// 	Initialize Thread Table for Each Process
//	
//----------------------------------------------------------------------
void AddrSpace::InitializeProcessThreadTable(int ProcessID)
{
	int i = 0;
	for( i = 0 ; i<MAX_NUM_THREADS ; i++)						
  {
		myProcessTable[ProcessID].sthreadTable[i].threadStatus = false;
		myProcessTable[ProcessID].sthreadTable[i].topOfStack = 0;
		myProcessTable[ProcessID].sthreadTable[i].ProcessID = ProcessID;
	}
}

//----------------------------------------------------------------------
// AddrSpace::PopulateProcessTable	
//----------------------------------------------------------------------
int AddrSpace::PopulateProcessTable(int ProcessID)
{
		ProcessTable *tempProcessTable;	
		tempProcessTable = new ProcessTable;
		
		tempProcessTable->processStatus = true;
		tempProcessTable->totalThreads = 1;
		tempProcessTable->activeThreads = 1;
		tempProcessTable->topOfStack = (numPages)*PageSize - 16;
		
		myProcessTable[ProcessID] = *tempProcessTable;
	
	return ProcessID;
}


//----------------------------------------------------------------------
// AddrSpace::DeallocateProcessTable
//----------------------------------------------------------------------
void AddrSpace::DeallocateProcessTable(int ProcessID)
{
	int i = 0;
	PageTableLock[AddressSpaceIndex]->Acquire();
	int nTotalPages = currentThread->space->numPages;
	for (i = 0; i < nTotalPages; i++)
	{
		pageBitMapTable->Clear(extpagetable[i].physicalPage);
	}
	delete [] extpagetable;
	myProcessTable[AddressSpaceIndex].processStatus = false;
	PageTableLock[AddressSpaceIndex]->Release();
}


//----------------------------------------------------------------------
// AddrSpace::UpdatePageTableOnFORK	
//----------------------------------------------------------------------
void AddrSpace::UpdatePageTableOnFORK(int ProcessID)
{
	PageTableLock[AddressSpaceIndex]->Acquire();
	unsigned int i = 0;
	if ( numPages + 8 > newnumpages)
	{
		printf("Thread Limit Reached : Thread cannot be forked");
		return;
	}
	

	for ( i = numPages; i < (numPages + 8); i++) 
	{
		extpagetable[i].pagestatus = udata;
	}

	numPages = numPages + 8;
	
	PageTableLock[AddressSpaceIndex]->Release();
}


//constructor for IPT

InvertedPageTable::InvertedPageTable(){
virtualPage = -1;
physicalPage = -1;
isEmpty = true;
}



#endif
